package io.kiki.sba.registry.common.model.store;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.kiki.sba.registry.common.model.ClientToClientInterfaceServerNodeId;
import io.kiki.sba.registry.common.model.RegisterVersion;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


public abstract class BaseInfo implements Serializable, StoreData<String> {

    private static final long serialVersionUID = 1L;

    @Getter
    private String dataInfoId;

    @Getter
    private String dataId;

    @Getter
    @Setter
    private String clientId;

    @Getter
    private String instanceId;

    @Getter
    private String zone;

    @Getter
    private String appName;

    @Getter
    private String processId;

    @Getter
    @Setter
    private String registerId;

    @Getter
    @Setter
    private long version;

    @Getter
    @Setter
    private Url clientUrl;

    @Getter
    @Setter
    private Url serverUrl;

    @Getter
    @Setter
    private ClientVersion clientVersion;

    @Getter
    private String group;

    @Getter
    @Setter
    private long registerTimestamp;

    @Getter
    @Setter
    private long clientRegisterTimestamp;

    private volatile Map<String, String> attributes;


    public void setZone(String zone) {
        this.zone = StringInterner.intern(zone);
    }


    public void setAppName(String appName) {
        this.appName = StringInterner.intern(appName);
    }


    public void setProcessId(String processId) {
        this.processId = StringInterner.intern(processId);
    }


    public Map<String, String> getAttributes() {
        Map<String, String> attributes = this.attributes;
        if (attributes == null) {
            return Collections.emptyMap();
        }
        return new HashMap<>(attributes);
    }


    public synchronized void setAttributes(Map<String, String> attributes) {
        if (attributes == null || attributes.isEmpty()) {
            this.attributes = null;
        } else {
            this.attributes = internAttributes(attributes);
        }
    }

    public String attributeOf(String key) {
        Map<String, String> attributes = this.attributes;
        if (attributes == null) {
            return null;
        }
        return attributes.get(key);
    }


    public void setDataInfoId(String dataInfoId) {
        this.dataInfoId = StringInterner.intern(dataInfoId);
    }

    @Override
    @JsonIgnore
    public String getId() {
        return registerId;
    }


    public void setDataId(String dataId) {
        this.dataId = StringInterner.intern(dataId);
    }


    public void setInstanceId(String instanceId) {
        this.instanceId = StringInterner.intern(instanceId);
    }


    public void setGroup(String group) {
        this.group = StringInterner.intern(group);
    }


    public ClientToClientInterfaceServerNodeId getClientToClientInterfaceServerNodeId() {
        return new ClientToClientInterfaceServerNodeId(getClientUrl().getIp(), getClientUrl().getPort(), getServerUrl().getIp(), getServerUrl().getPort());
    }

    public int attributesSize() {
        final Map<String, String> map = this.attributes;
        return map == null ? 0 : map.size();
    }

    protected Map<String, String> internAttributes(Map<String, String> attributes) {
        if (CollectionUtils.isEmpty(attributes)) {
            return Collections.emptyMap();
        }
        Map<String, String> map = new HashMap<>(attributes.size());
        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            map.put(StringInterner.intern(entry.getKey()), entry.getValue());
        }
        return map;
    }

    public RegisterVersion registerVersion() {
        return RegisterVersion.of(getVersion(), getRegisterTimestamp());
    }


    @Getter
    public enum ClientVersion {

        ProtocolPackage("1.x"),

        NProtocolpackage("2.x"),

        MProtocolpackage("3.x"),

        StoreData("4.x");

        private String version;


        ClientVersion(String version) {
            this.version = version;
        }

    }
}
